package com.wy.aspect;

import com.wy.model.InterfaceLog;
import com.wy.service.InterfaceLogService;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @描述
 * @创建人 wangyue
 * @创建时间2019/12/1311:32
 */
@Slf4j
@Aspect //切面类
@Component //把这个类实例化，交给spring管理
public class InterfaceLogAspect {

    private InterfaceLog interfaceLog = new InterfaceLog();//定义日志类全局对象

    @Autowired
    private InterfaceLogService interfaceLogService;

    //切入点，定义切入类执行的地点
    //1* 任意返回值
    //2* 任意类
    //3* 任意方法
    //..表示任意参数
    //这里的切入点定义了，只我们执行com.wy.controller包下边的任意类任意方法，都会被这个切面类拦截
    //@Pointcut("execution(public * com.wy.controller.*.*(..))")
    //用自定义注解实现切入点
     @Pointcut("@annotation(com.wy.aspect.MyLog)")
    public void interfaceLog() {
    }
    //前置方法
    @Before(value = "interfaceLog()")
    public void before(JoinPoint point) {
        log.info("方法之前执行......");
        //  获取请求对象
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        //获取ip地址
        interfaceLog.setRemote_addr(IPUtils.getIpAddr(request));
        interfaceLog.setUrl(request.getRequestURI());//获取日志路径
        //获取session
        HttpSession session = request.getSession();
        interfaceLog.setCreator_id("1");
        interfaceLog.setCreator("admin");//由于没有登录，session里边目前不存在，所以这里模拟一下
        //获取自定义注解里边的值
        String _signatureName = point.getSignature().getName();//获取当前操作方法的名称
        interfaceLog.setRequest_method(_signatureName);

        Class targetClass = point.getTarget().getClass();//获取当前操作的类
        Method[] targetMethods = targetClass.getDeclaredMethods();//获取当前操作类的所有方法
        for (Method _method : targetMethods) {
            if (_method.getName().equalsIgnoreCase(_signatureName)) {//循环当前类的所有方法，如果和上边获取的当前操作方法的名称一致，那么就是这个方法
                MyLog mylog = _method.getAnnotation(MyLog.class);//获取当前操作方法上的特定注解
                interfaceLog.setRequest_type(mylog.enumType().code);
                interfaceLog.setRemark(mylog.remark());
                interfaceLog.setValid(mylog.enumValid().code);
            }
        }
        //获取所有的参数 point.getArgs()
        // 请求报文
        if (point.getArgs() != null && point.getArgs().length > 0) { //判断方法有参数
            Object parameterObject = point.getArgs()[0];
            if (parameterObject instanceof String) { //如果是String类型直接保存，如果不是，就转换成json字符串进行保存
                interfaceLog.setRequest_body((String) parameterObject);
            }else if(parameterObject instanceof String[]){
                JSONArray jsonArray = JSONArray.fromObject(parameterObject);
                interfaceLog.setRequest_body(jsonArray.toString());
            } else {
                JSONObject jsonObject = JSONObject.fromObject(parameterObject);
                interfaceLog.setRequest_body(jsonObject.toString());
            }
        }
        //请求时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        interfaceLog.setRequest_date(sdf.format(new Date()));

    }
    //后置通知
    @AfterReturning(value = "interfaceLog()", returning = "ret")
    public void after(Object ret) {
        log.info("方法正常执行完之后执行......");
        //响应结果
        JSONObject jsonObject = JSONObject.fromObject(ret);
        interfaceLog.setResponse_body(jsonObject.toString());
        interfaceLog.setResponse_status("SUCCESS");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        interfaceLog.setResponse_date(sdf.format(new Date()));
        save();//成功的日志
    }
    //异常通知
    @AfterThrowing(value = "interfaceLog()", throwing = "throwing")
    public void error(Throwable throwing) {
        log.info("方法抛了异常之后执行......");
        interfaceLog.setResponse_status("ERROR");
        interfaceLog.setResponse_message(throwing.getMessage());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        interfaceLog.setRequest_date(sdf.format(new Date()));
        save();//失败的日志
    }
    //插入日志的方法
    private void save(){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        interfaceLog.setCreate_date(sdf.format(new Date()));
        interfaceLogService.insert(interfaceLog);
    }

   /* //最终通知---无论方法正常执行完，还是抛了异常，最终一定会执行
    @After("interfaceLog()")
    public void after() {
        log.info("方法最终执行......");
    }*/

   /* @Around("interfaceLog()")
    public Object doAround(ProceedingJoinPoint joinPoint){
        log.info("环绕通知......");
        log.info("在方法之前进入，但是执行完后，不会再主动进入原方法，需要通过反射调用......");
        Object res =null;
        try {
            res =joinPoint.proceed();//通过反射调用原方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return  res;
    }*/
}
